/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "IModelBase.h"

#include "core/core.h"

#include <algorithm>

namespace model {

IModelBase::IModelBase(const eModelType& type)
:	sg::SceneNode(),
	m_modelType(type)
{
}

IModelBase::~IModelBase(void)
{
}

IModelBase* IModelBase::Load(const std::string &filename)
{
	IModelBase *model = NULL;

	errno_t err;
	FILE *file;
	sModelHeader header;

	if ( (err = fopen_s(&file, filename.c_str(), "rb")) != 0)
	{
		coreGetLog()->WriteError("Could not open file. File not found?", __FILE__, __LINE__);
		//fclose(file);
		return NULL;
	}

	fread( &header, sizeof(header), 1, file);

	switch( header.modelType ) 
	{
		case MODEL_KEYFRAME:
		case MODEL_STATIC:
			model = new IModelBase(header.modelType);
			break;
		default:
			coreGetLog()->WriteError("Unknown model type.", __FILE__, __LINE__);
			fclose(file);
			return NULL;
	};

	if (model) {
		// Store the number of meshes from the header
		model->m_uiNumMeshes	 = header.numMeshes;


		if (model->m_uiNumMeshes == 0) 
			return NULL;

		if ( !model->loadMeshes( file ) ) {
			coreGetLog()->WriteError("Error parsing mesh data for model.", __FILE__, __LINE__);
			fclose(file);
			return NULL;
		}

		if ( !model->loadData( file ) ) {
			coreGetLog()->WriteError("Error parsing additional data for model.", __FILE__, __LINE__);
			fclose(file);
			return NULL;
		}
	}
	fclose(file);

	coreGetLog()->WriteLog("Model loaded: " + filename, __FILE__, __LINE__);

	return model;
}

const IModelBase::mesh_list* IModelBase::getMesh_List_BoundingVols()
{
	return &m_lBoundingVols;
}

bool IModelBase::loadMeshes(FILE *file) 
{
	for (unsigned int i=0; i<m_uiNumMeshes; ++i) 
	{
		// Create object 
		IMesh* mesh = new IMesh();
		char *meshName;

		// load the header from the binary file
		int length;
 		fread(&length, sizeof(int), 1, file);
		meshName = (char*) malloc(length+1);
		memset(meshName, 0, length+1);
		fread(meshName, 1, length, file);
		
		mesh->setName(meshName);

		// load the mesh data
		if ( !mesh->load( file ) ) {
			mesh->destroy();
			return false;
		}

		// add the mesh to the model 
		m_lMeshes[meshName] = mesh;
		// add the mesh to the scene node
		if ( !mesh->isBoundingVolume() ) {
			addChild( mesh, mesh->isTransparent() );
		}
		else 
			m_lBoundingVols[meshName] = mesh;

		free(meshName);
	}
	return true;
}

eModelType IModelBase::getModelType() const
{
	return m_modelType;
}

IMesh* IModelBase::getMeshByName(const std::string &name)
{
	IMesh *mesh;
	child_list::iterator it;
	for (it = lChildren.begin(); it != lChildren.end(); ++it) {
		mesh = dynamic_cast<IMesh*>(*it);
		if (mesh->getName() == name) {
			return mesh;
		}
	}

	for (it = lTranspChildren.begin(); it != lTranspChildren.end(); ++it) {
 		mesh = dynamic_cast<IMesh*>(*it);
		if (mesh->getName() == name) {
			return mesh;
		}
	}

	mesh_list::iterator mf = m_lBoundingVols.find(name);
	if (mf != m_lBoundingVols.end()) {
		return m_lBoundingVols[name];
	}
	return NULL;
}

IMesh* IModelBase::getMesh(unsigned int num)
{
	child_list::iterator it = lChildren.begin();
	std::advance( it , num);
	return dynamic_cast<IMesh*>(*it);
}

unsigned int IModelBase::getNumMeshes() const
{
	return m_uiNumMeshes;
}

sg::SceneNode* IModelBase::rawcopy()
{
	IModelBase *node;

	switch (this->getModelType()) {
		case MODEL_BASE:
			break;
		case MODEL_STATIC:
		case MODEL_KEYFRAME:
			node = new IModelBase(this->getModelType());
			break;
	}

	node->m_uiNumMeshes = this->m_uiNumMeshes;
	node->m_modelType = this->m_modelType;

	for (mesh_list::iterator it = m_lBoundingVols.begin(); it != m_lBoundingVols.end(); ++it) {
		node->m_lBoundingVols[ (it->second)->getName() ] = (IMesh*) (it->second)->copy();
	}

	return node;
}

bool IModelBase::loadData(FILE* file)
{
	return loadKeyFrameData(file, this);
}

void IModelBase::update(double dt)
{
	// update the current frame
	updateKeyFrames(dt);
	// update the keys from the children
	for (child_list::iterator it = lChildren.begin(); it != lChildren.end(); ++it) {
		dynamic_cast<IMesh*>(*it)->updateKeyFrame(m_dCurrentFrame);
	}
}

} // namespace
